package com.zoe.onelink.logging.trace.autoconfigure;


import brave.Tracer;
import brave.handler.SpanHandler;
import brave.http.HttpRequest;
import brave.sampler.SamplerFunction;
import com.zoe.onelink.logging.trace.handler.SpanFinishedHandler;
import com.zoe.onelink.logging.trace.property.OnelinkZipkinProperties;
import com.zoe.onelink.logging.trace.property.TraceLogProperties;
import com.zoe.onelink.logging.trace.zipkin.custom.OnelinkZipkinRestTemplateCustomizer;
import com.zoe.onelink.logging.trace.zipkin.reporter.SpanLogReporter;
import com.zoe.onelink.logging.trace.zipkin.sampler.ZipkinHttpSampler;
import com.zoe.onelink.logging.trace.zipkin.sender.SpanNopSender;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.instrument.web.HttpServerSampler;
import org.springframework.cloud.sleuth.zipkin2.ZipkinAutoConfiguration;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import zipkin2.Span;
import zipkin2.reporter.AsyncReporter;
import zipkin2.reporter.Reporter;
import zipkin2.reporter.ReporterMetrics;
import zipkin2.reporter.Sender;

import java.util.concurrent.TimeUnit;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2022</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2022-05-26
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Tracer.class)
@AutoConfigureBefore(ZipkinAutoConfiguration.class)
@Slf4j
public class OnelinkTraceZipkinConfiguration {

    public static final String ZIPKIN_SPAN_STATISTICS_HANDLER = "zipkinSpanStatisticsHandler";

    /**
     * 如果使用 @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) 可以覆盖原有的Span实现
     * 使用其他名称的Bean则加入到ListReporter中依次执行
     */
    @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME)
    @ConditionalOnProperty(name = "spring.zipkin.sender.type", havingValue = "slf4j", matchIfMissing = false)
    public Reporter<Span> spanLogReporter() {
        log.info("[OneLinkFrm] [ZipkinReporter] 使用Logback策略");
        return new SpanLogReporter();
    }

    @Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME)
    @ConditionalOnProperty(name = "spring.zipkin.sender.type", havingValue = "slf4j", matchIfMissing = false)
    public Sender spanNopSender() {
        return new SpanNopSender();
    }

    /**
     * 如果使用 @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME) 可以覆盖原有的Span实现
     * 使用其他名称的Bean则加入到ListReporter中依次执行
     */
    @Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME)
    @ConditionalOnExpression("#{ '${spring.zipkin.sender.type:}' == 'web' && ${onelink.logging.zipkin.enhance-enabled:false} }")
    public Reporter<Span> reporter(ReporterMetrics reporterMetrics,
                                   ZipkinProperties zipkin,
                                   OnelinkZipkinProperties onelinkZipkinProperties,
                                   @Qualifier(ZipkinAutoConfiguration.SENDER_BEAN_NAME) Sender sender) {
        return AsyncReporter.builder(sender)
                .queuedMaxSpans(onelinkZipkinProperties.getQueuedMaxSpans())
                .queuedMaxBytes((int) onelinkZipkinProperties.getQueuedMaxSize().toBytes())
                .messageMaxBytes((int) onelinkZipkinProperties.getMessageMaxSize().toBytes())
                .messageTimeout(zipkin.getMessageTimeout(), TimeUnit.SECONDS)
                .metrics(reporterMetrics).build(zipkin.getEncoder());
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnExpression("#{ '${spring.zipkin.sender.type:}' == 'web' && ${onelink.logging.zipkin.enhance-enabled:false} }")
    public ZipkinRestTemplateCustomizer onelinkZipkinRestTemplateCustomizer(ZipkinProperties zipkinProperties,
                                                                            OnelinkZipkinProperties onelinkZipkinProperties) {
        log.info("[OneLinkFrm] [ZipkinSender] 开启HttpSender增强");
        return new OnelinkZipkinRestTemplateCustomizer(zipkinProperties, onelinkZipkinProperties);
    }

    @Bean(OnelinkTraceZipkinConfiguration.ZIPKIN_SPAN_STATISTICS_HANDLER)
    @ConditionalOnMissingBean(name = OnelinkTraceZipkinConfiguration.ZIPKIN_SPAN_STATISTICS_HANDLER)
    public SpanHandler spanFinishedHandler() {
        return new SpanFinishedHandler();
    }

    @Bean(name = HttpServerSampler.NAME)
    @ConditionalOnMissingBean(name = HttpServerSampler.NAME)
    public SamplerFunction<HttpRequest> onelinkHttpSampler(TraceLogProperties traceLogProperties) {
        return new ZipkinHttpSampler(traceLogProperties);
    }
}